import json
import os
import time
import socket
import requests
from time import sleep
from requests_toolbelt.multipart.encoder import MultipartEncoder


class Logger:
    def __init__(self, level='debug'):
        self.level = level

    def DebugLog(self, *args):
        if self.level == 'debug':
            print(*args)

    def TraceLog(self, *args):
        if self.level == 'trace':
            print(*args)

    def setDebugLevel(self, level):
        self.level = level.lower()


class QQBot:
    def __init__(self, target, addr='http://127.0.0.1:8080/', remote_server='http://xfxuezhang.cn/WEB/MagicMirror/images/'):
        self.addr = addr
        self.target = target
        self.currentQQ = ''
        self.remote_server = remote_server

    def getSession(self, auth_key):
        """每个Session只能绑定一个Bot，但一个Bot可有多个Session。
        session Key在未进行校验的情况下，一定时间后将会被自动释放"""
        data = {"authKey": auth_key}
        url = self.addr+'auth'
        res = requests.post(url, data=json.dumps(data)).json()
        logger.DebugLog(res)
        if res['code'] == 0:
            return res['session']
        return None

    def verifySession(self, session, qq):
        """校验并激活Session，同时将Session与一个已登录的Bot绑定"""
        data = {"sessionKey": session, "qq": qq}
        url = self.addr + 'verify'
        res = requests.post(url, data=json.dumps(data)).json()
        logger.DebugLog(res)
        if res['code'] == 0:
            return True
        return False

    def releaseSession(self, session, qq):
        """不使用的Session应当被释放，长时间（30分钟）未使用的Session将自动释放，
        否则Session持续保存Bot收到的消息，将会导致内存泄露(开启websocket后将不会自动释放)"""
        data = {"sessionKey": session, "qq": qq}
        url = self.addr + 'release'
        res = requests.post(url, data=json.dumps(data)).json()
        logger.DebugLog(res)
        if res['code'] == 0:
            return True
        return False

    def getMsgFromUser(self, session):
        url = self.addr + 'fetchLatestMessage?count=10&sessionKey='+session
        res = requests.get(url).json()
        if res['code'] == 0:
            return res['data']
        return None

    def parseUserMsg(self, data):
        text = ''
        if data is None:
            return text
        for item in data:
            if item['type'] == 'FriendMessage' and str(item['sender']['id']) in self.target:
                self.currentQQ = str(item['sender']['id'])
                type = item['messageChain'][-1]['type']
                if type == 'Plain':
                    text = item['messageChain'][-1]['text']
                else:
                    logger.TraceLog(">> 当前消息类型暂不支持解析：=> "+type)
                    continue
        return text

    def getMessageCount(self, session):
        url = self.addr + 'countMessage?sessionKey='+session
        res = requests.get(url).json()
        if res['code'] == 0:
            return res['data']
        return 0

    def sendMsgToUser(self, session, qq, msg):
        data = {
                  "sessionKey": session,
                  "target": qq,
                  "messageChain": [{"type": "Plain", "text": msg}]
                }
        url = self.addr + 'sendFriendMessage'
        try:
            requests.post(url, data=json.dumps(data)).json()
        except:
            logger.DebugLog(">> 发送失败")
            return 0

    def sendImgToUser(self, session, qq, img_name):
        img_url = self.remote_server + 'images/' + img_name
        data = {
            "sessionKey": session,
            "target": qq,
            "messageChain": [{"type": "Image", "url": img_url}]
        }
        url = self.addr + 'sendFriendMessage'
        try:
            requests.post(url, data=json.dumps(data)).json()
        except:
            logger.DebugLog(">> 发送失败")
            return 0


    def execCmd(self, cmd):
        if cmd:
            socketClientSend(cmd)


def socketServerListen(bot, session):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('localhost', server_port))
    server.listen(1)
    while True:
        try:
            client, addr = server.accept()
            data = client.recv(max_transfer).decode('utf-8').strip()
            client.close()
            if data:
                content = ' '.join(data.split(' ')[1:])
                if data.startswith('[msg]'):
                    bot.sendMsgToUser(session, bot.currentQQ, content)
                elif data.startswith('[image]'):
                    bot.sendImgToUser(session, bot.currentQQ, content)
                    bot.execCmd('admin delCloudImg '+content)
        except Exception as e:
            print("socketServerListen: ", e)

def socketClientSend(msg):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(('localhost', client_port))
    client.send(msg.encode('utf-8'))
    client.close()

import threading
logger = Logger()
if __name__ == '__main__':
    if not os.path.exists('conf.json'):
        print(">> 缺少conf.json")
        exit()

    with open('conf.json', 'r+', encoding="utf-8-sig") as f:
        content = f.read()
    conf = json.loads(content)

    auth_key = conf['auth_key']
    bind_qq = conf['bind_qq']
    target = conf['target']
    sleep_time = conf['sleep_time']
    debug_level = conf['debug_level']
    addr = conf['mirai_addr']
    remote_server = conf['remote_server']
    server_port = conf['socket_server_port']
    client_port = conf['socket_client_port']
    max_transfer = conf['socket_max_transfer']


    logger.setDebugLevel(debug_level)

    bot = QQBot(target, addr, remote_server)
    session = bot.getSession(auth_key)
    logger.DebugLog(">> session: "+session)
    bot.verifySession(session, bind_qq)

    th1 = threading.Thread(target=socketServerListen, args=(bot, session))
    th1.setDaemon(True)
    th1.start()

    while True:
        cnt = bot.getMessageCount(session)
        if cnt:
            logger.DebugLog('>> 有消息了 => {}'.format(cnt))
            logger.DebugLog('获取消息内容')
            data = bot.getMsgFromUser(session)
            if len(data) == 0:
                logger.DebugLog('消息为空')
                continue
            logger.DebugLog(data)
            logger.DebugLog('解析消息内容')
            data = bot.parseUserMsg(data)
            logger.DebugLog(data)
            bot.execCmd(data)
        sleep(sleep_time)
    bot.releaseSession(session, bind_qq)



